﻿using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using FFmpegVideoClip.Configuration;
using FFmpegVideoClip.windows;

namespace FFmpegVideoClip.Lib.Controls
{
    internal class TimeLineTickMarks: TickBar
    {
        public class MouseRightButtonClickEventArgs : MouseButtonEventArgs
        {
            public TickLine? SelectTickLine { get; private set; }
            public MouseRightButtonClickEventArgs(MouseButtonEventArgs eventArgs, TickLine? SelectTickLine) : base(eventArgs.MouseDevice, eventArgs.Timestamp, eventArgs.ChangedButton, eventArgs.StylusDevice)
            { this.SelectTickLine = SelectTickLine; }
        }
        internal class TickLine :IComparable<TickLine>
        {
            internal class Comparer : IComparer<TickLine>
            {
                public static readonly Comparer Default = new();
                public int Compare(TickLine? x, TickLine? y)
                {
                    if (x == null && y == null)
                        return 0;
                    if (x == null)
                        return -1;
                    if (y == null)
                        return 1;
                    if (x.SecondsTime > y.SecondsTime)
                        return 1;
                    else if (x.SecondsTime < y.SecondsTime)
                        return -1;
                    else return 0;
                }
            }
            public const double EditThickness = 2d;
            public const double DefaultThickness = 1d;
            public static Comparer Default => Comparer.Default;
            //Ticks接收到的时间是以秒为单位的值
            public double SecondsTime;
            public Pen Pen;
            public double PositionX;
            public TickLine(double SecondsTime, Brush brush) { this.SecondsTime = SecondsTime; Pen = new Pen(brush, DefaultThickness); }
            public TickLine(double SecondsTime, Brush brush,double Thickness) { this.SecondsTime = SecondsTime; Pen = new Pen(brush, Thickness); }

            public int CompareTo(TickLine? other)
            {
                if (other == null || SecondsTime > other.SecondsTime)
                    return 1;
                else if (SecondsTime < other.SecondsTime)
                    return -1;
                else return 0;
            }

            public void SetWidth(double value) { Pen.Thickness = value; }


        };


        private TickLine? CurrentDragTickLine;
        private List<TickLine> TickLines = new();
        private TimeLineSlider? m_TimeLineSlider;
        private DependencyPropertyDescriptor? MaxTimeDescriptor, CurrentZoomMinTimeDescriptor, CurrentZoomMaxTimeDescriptor;
        private MenuItem TickMarksEdit_menuItem_AddorRemove = new MenuItem();
        private MenuItem TickMarksEdit_menuItem_CloseEdit = new MenuItem() { Header= "关闭分割点编辑" };
        private MenuItem TickMarksEdit_menuItem_CutSplitFile = new MenuItem() { Header = "分割到剪辑" };
        private MenuItem TickMarksEdit_menuItem_RemoveAll = new MenuItem() { Header = "清空分割点" };
        private ContextMenu m_EditMenu = new ContextMenu();

        public bool HasTimeRange => Maximum - Minimum > 0;


        public TimeLineTickMarks() : base() { Loaded += TimeLineTickMarks_Loaded;}
        private void TimeLineTickMarks_Loaded(object sender, RoutedEventArgs e)
        {
            Loaded -= TimeLineTickMarks_Loaded;

            if (!Tools.GetParent(this, out m_TimeLineSlider))
                throw new InitializationException($"类型[{GetType().FullName}]的实例所在的父级中不存在[{typeof(TimeLineSlider).FullName}]");

            MaxTimeDescriptor = DependencyPropertyDescriptor.FromProperty(TimeLineSlider.MaxTimeProperty, typeof(TimeLineSlider));
            MaxTimeDescriptor.AddValueChanged(m_TimeLineSlider, OnMaxTimeChange);

            CurrentZoomMinTimeDescriptor = DependencyPropertyDescriptor.FromProperty(TimeLineSlider.CurrentZoomMinTimeProperty, typeof(TimeLineSlider));
            CurrentZoomMinTimeDescriptor.AddValueChanged(m_TimeLineSlider, OnZoomMinTimeChange);

            CurrentZoomMaxTimeDescriptor = DependencyPropertyDescriptor.FromProperty(TimeLineSlider.CurrentZoomMaxTimeProperty, typeof(TimeLineSlider));
            CurrentZoomMaxTimeDescriptor.AddValueChanged(m_TimeLineSlider, OnZoomMaxTimeChange);

            m_EditMenu.Items.Add(TickMarksEdit_menuItem_AddorRemove);
            TickMarksEdit_menuItem_AddorRemove.Click += TickMarksEdit_menuItem_AddorRemove_Click;
            m_EditMenu.Items.Add(TickMarksEdit_menuItem_RemoveAll);
            TickMarksEdit_menuItem_RemoveAll.Click += TickMarksEdit_menuItem_RemoveAll_Click;
            m_EditMenu.Items.Add(TickMarksEdit_menuItem_CloseEdit);
            TickMarksEdit_menuItem_CloseEdit.Click += TickMarksEdit_menuItem_CloseEdit_Click;
            m_EditMenu.Items.Add(TickMarksEdit_menuItem_CutSplitFile);
            TickMarksEdit_menuItem_CutSplitFile.Click += TickMarksEdit_menuItem_CutSplitFile_Click;

        }

        private void TickMarksEdit_menuItem_CutSplitFile_Click(object sender, RoutedEventArgs e)
        {
            CutSplitFileMessageWindow.MessageResultEnum SelectResult = CutSplitFileMessageWindow.ShowMessage();
            if (SelectResult == CutSplitFileMessageWindow.MessageResultEnum.Cancel)
                return;
            if(m_TimeLineSlider ==null || string.IsNullOrWhiteSpace(m_TimeLineSlider.FilePath) || !File.Exists(m_TimeLineSlider.FilePath))
            {
                MessageBoxEx.Show(Application.Current.MainWindow, $"当前播放文件路径异常:[{m_TimeLineSlider?.FilePath}]", "获取文件错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }
            switch (SelectResult)
            {
                case CutSplitFileMessageWindow.MessageResultEnum.CurrentClip:

                    if(ClipManage.CurrentEditFragment.clip == null)
                    {
                        MessageBoxEx.Show(Application.Current.MainWindow, $"未指定当前剪辑", "错误操作", MessageBoxButton.OK, MessageBoxImage.Error);
                        return;
                    }
                    ClipManage.CutSplitFile(m_TimeLineSlider!.FilePath, TickLines.Select(p=>p.SecondsTime).OrderBy(p=>p), ClipManage.CurrentEditFragment.clip);
                    return;
                case CutSplitFileMessageWindow.MessageResultEnum.NewClip:
                    ClipManage.CutSplitFile(m_TimeLineSlider!.FilePath, TickLines.Select(p => p.SecondsTime).OrderBy(p=>p));
                    return;
                case CutSplitFileMessageWindow.MessageResultEnum.SingleClips:
                    ClipManage.CutSplitFile(m_TimeLineSlider!.FilePath, TickLines.Select(p => p.SecondsTime).OrderBy(p=>p), IsEnableCutToSingleClips: true);
                    return;
                default:
                    return;
            }
        }

        private void OnMaxTimeChange(object? sender, EventArgs e) => MaxTime = sender == null ? TimeSpan.Zero : ((TimeLineSlider)sender).MaxTime;
        private void OnZoomMinTimeChange(object? sender, EventArgs e) => CurrentZoomMinTime = sender == null ? TimeSpan.Zero : ((TimeLineSlider)sender).CurrentZoomMinTime;
        private void OnZoomMaxTimeChange(object? sender, EventArgs e) => CurrentZoomMaxTime = sender == null ? TimeSpan.Zero : ((TimeLineSlider)sender).CurrentZoomMaxTime;
        


        [Bindable(true)] public bool IsDragging { get => (bool)GetValue(IsDraggingProperty); private set => SetValue(IsDraggingProperty, value); }
        public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.Register(nameof(IsDragging), typeof(bool), typeof(TimeLineTickMarks), new PropertyMetadata(false));

        [Bindable(true)] public TimeSpan MaxTime { get => (TimeSpan)GetValue(MaxTimeProperty); set => SetValue(MaxTimeProperty, value); }
        public static readonly DependencyProperty MaxTimeProperty = DependencyProperty.Register(nameof(MaxTime), typeof(TimeSpan), typeof(TimeLineTickMarks), new PropertyMetadata(TimeSpan.Zero));

        [Bindable(true)] public TimeSpan CurrentZoomMinTime { get => (TimeSpan)GetValue(CurrentZoomMinTimeProperty); set => SetValue(CurrentZoomMinTimeProperty, value); }
        public static readonly DependencyProperty CurrentZoomMinTimeProperty = DependencyProperty.Register(nameof(CurrentZoomMinTime), typeof(TimeSpan), typeof(TimeLineTickMarks), new PropertyMetadata(TimeSpan.Zero, (s, e) =>
        {
            TimeLineTickMarks TickMarks = (TimeLineTickMarks)s;
            TimeSpan ts = (TimeSpan)e.NewValue;
            if (TickMarks.Minimum != ts.TotalSeconds)
                TickMarks.Minimum = ts.TotalSeconds;
        }));
        
        [Bindable(true)] public TimeSpan CurrentZoomMaxTime { get => (TimeSpan)GetValue(CurrentZoomMaxTimeProperty); set => SetValue(CurrentZoomMaxTimeProperty, value); }
        public static readonly DependencyProperty CurrentZoomMaxTimeProperty = DependencyProperty.Register(nameof(CurrentZoomMaxTime), typeof(TimeSpan), typeof(TimeLineTickMarks), new PropertyMetadata(TimeSpan.Zero, (s, e) =>
        {
            TimeLineTickMarks TickMarks = (TimeLineTickMarks)s;
            TimeSpan ts = (TimeSpan)e.NewValue;
            if (TickMarks.Maximum != ts.TotalSeconds)
                TickMarks.Maximum = ts.TotalSeconds;
        }));

        [Bindable(true)] public bool IsEnableTickMarksEdit { get => (bool)GetValue(IsEnableTickMarksEditProperty); set => SetValue(IsEnableTickMarksEditProperty, value); }
        public static readonly DependencyProperty IsEnableTickMarksEditProperty = DependencyProperty.Register(nameof(IsEnableTickMarksEdit), typeof(bool), typeof(TimeLineTickMarks), new PropertyMetadata(false, (s, e) => 
        {
            TimeLineTickMarks tickMarks = (TimeLineTickMarks)s;
            bool value = (bool)e.NewValue;
            if (value && !(tickMarks.m_TimeLineSlider?.IsFileOpen ?? false) || !tickMarks.HasTimeRange)
            {
                tickMarks.IsEnableTickMarksEdit = false;
                return;
            }
            if ((bool)e.NewValue)
                tickMarks.TickLines.ForEach(p => p.SetWidth(TickLine.EditThickness));
            else
                tickMarks.TickLines.ForEach(p => p.SetWidth(TickLine.DefaultThickness));
            tickMarks.InvalidateVisual();
        }));



        private ImmutableArray<TickLine> TickLinesWithinTheTimeRangeCache = [];
        protected override void OnRender(DrawingContext m_drawingContext) 
        {
            double SecondsUIWidth = ActualWidth / (Maximum - Minimum);
            double UIPointX;
            ReadOnlySpan<TickLine> RenderLine = TickLines.Where(p => p.SecondsTime > Minimum && p.SecondsTime < Maximum).ToArray();
            TickLinesWithinTheTimeRangeCache = RenderLine.ToImmutableArray();
            if (RenderLine.Length < 1)
                return;
            foreach (TickLine Item in RenderLine)
            {
                UIPointX = (Item.SecondsTime - Minimum) * SecondsUIWidth;
                Item.PositionX = UIPointX;
                m_drawingContext.DrawLine(Item.Pen, new(UIPointX, 0), new(UIPointX, ActualHeight));
            }
        }
        private double UIPositionToTime(double UIPosition)//[MethodImpl(MethodImplOptions.AggressiveInlining)]
        {
            double CurrentTimeRangeDuration = Maximum - Minimum;
            return Minimum + CurrentTimeRangeDuration * (UIPosition / ActualWidth);
        }
        public void Set(double[] values) 
        {
            TickLines.Clear();
            if (values.Length > 0)
            {
                foreach (double value in values)
                    TickLines.Add(new(value, Fill));
                TickLines.Sort();    
            }
            InvalidateVisual();
        }
        public void Clear() { TickLines.Clear(); IsEnableTickMarksEdit = false; InvalidateVisual(); }




        private bool TickLineHitTest(double PointX,out TickLine? line)
        {
            line = null!;
            foreach (TickLine item in TickLinesWithinTheTimeRangeCache)
            {
                if (Math.Abs(PointX - item.PositionX) < Config.TickLineHitTestSizeValue)
                {
                    line = item;
                    return true;
                }
            }
            return false;
        }

        public void AddTickLine(double UIPosition, double Thickness = TickLine.DefaultThickness)
        {
            TickLines.Add(new(UIPositionToTime(UIPosition), Fill, Thickness));
            TickLines.Sort();
            InvalidateVisual();
        }
        public void RemoveTickLine(TickLine m_TickLine)
        {
            TickLines.Remove(m_TickLine);
            InvalidateVisual();
        }
        private void TickMarksEdit_menuItem_CloseEdit_Click(object sender, RoutedEventArgs e) => IsEnableTickMarksEdit = false;

        private void TickMarksEdit_menuItem_RemoveAll_Click(object sender, RoutedEventArgs e) => Set([]);
        private void TickMarksEdit_menuItem_AddorRemove_Click(object sender, RoutedEventArgs e)
        {
            MouseRightButtonClickEventArgs args = (MouseRightButtonClickEventArgs)TickMarksEdit_menuItem_AddorRemove.DataContext;
            if (args.SelectTickLine == null)
                AddTickLine(args.GetPosition(this).X, TickLine.EditThickness);
            else RemoveTickLine(args.SelectTickLine);
        }
        protected virtual void OnMouseRightButtonClick_OpenMenuItems(MouseRightButtonClickEventArgs e)
        {
            TickMarksEdit_menuItem_AddorRemove.DataContext = e;
            if (e.SelectTickLine == null)
            {
                TickMarksEdit_menuItem_AddorRemove.Header = "添加";
            }
            else
                TickMarksEdit_menuItem_AddorRemove.Header = "删除";
            m_EditMenu.IsOpen = true;
        }
        protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
        {
            if (e.RightButton == MouseButtonState.Pressed && IsEnableTickMarksEdit)
            {
                e.Handled = true;
                Focus();
                CaptureMouse();
            }
            base.OnMouseRightButtonDown(e);
        }
        protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
        {
            if (IsMouseCaptured && e.RightButton == MouseButtonState.Released && IsEnableTickMarksEdit)
            {
                ReleaseMouseCapture();
                TickLineHitTest(e.GetPosition(this).X, out TickLine? tickLine);
                OnMouseRightButtonClick_OpenMenuItems(new MouseRightButtonClickEventArgs(e, tickLine));
                e.Handled = true;
            }
            base.OnMouseRightButtonUp(e);
        }

        private void OnTickLineDragStart(TickLine DragTickLine)
        {
            CurrentDragTickLine = DragTickLine;
        }
        private void OnTickLineDragDelta(double NewPosition)
        {
            if (CurrentDragTickLine == null)
            { CancelDrag(); return; }

            CurrentDragTickLine.SecondsTime = UIPositionToTime(NewPosition);
            InvalidateVisual();
        }
        private void OnTickLineDragExit(double? CompletedPosition)
        {
            CurrentDragTickLine = null;
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if (!IsEnableTickMarksEdit || !m_TimeLineSlider!.IsFileOpen || !HasTimeRange || !TickLineHitTest(e.GetPosition(this).X, out TickLine? tickLine))
                return;
            if (!IsDragging)
            {
                e.Handled = true;
                Focus();
                CaptureMouse();
                IsDragging = true;
                bool flag = true;
                try
                {
                    OnTickLineDragStart(tickLine!);
                    flag = false;
                }
                finally
                {
                    if (flag)
                    {
                        CancelDrag();
                    }
                }
            }
            base.OnMouseLeftButtonDown(e);
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (!IsDragging)
            {
                return;
            }
            if (e.MouseDevice.LeftButton == MouseButtonState.Pressed)
            {
                OnTickLineDragDelta(e.GetPosition(this).X);
                e.Handled = true;
            }
            else
            {
                if (e.MouseDevice.Captured == this)
                    ReleaseMouseCapture();
                IsDragging = false;
            }
        }
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            if (IsMouseCaptured && IsDragging)
            {
                e.Handled = true;
                IsDragging = false;
                ReleaseMouseCapture();
                Point point = e.MouseDevice.GetPosition(this);
                OnTickLineDragExit(point.X);
            }
            base.OnMouseLeftButtonUp(e);
        }
        public void CancelDrag()
        {
            if (IsDragging)
            {
                if (base.IsMouseCaptured)
                    ReleaseMouseCapture();
                IsDragging = false;
                OnTickLineDragExit(null);
            }
        }


        [Serializable]
        public class InitializationException : Exception
        {
            public InitializationException() { }
            public InitializationException(string message) : base(message) { }
            public InitializationException(string message, Exception inner) : base(message, inner) { }
            [Obsolete] protected InitializationException(
              System.Runtime.Serialization.SerializationInfo info,
              System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
        }
    }
}
